#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc, char const *argv[])
{
    /**
    作用：根据当前进程，创建一个新的子进程
    返回值：pid_t 本质是int类型
        失败：-1
        进入子（新的）进程执行： 0
        进入父（当前）进程执行： 子进程的pid
    注意：
        当成功后 会拷贝一个和当前进程一样的进程
        当执行系统调用后  cpu会到 linux内核手里 在一个进程中经常会调用系统函数
        当linux内核执行完你的操作后会把cpu给任何一个进程，用linux内核决定
        所以当你fork成功后 有可能cpu会还给你  也有可能cpu会给到拷贝的子进程

    pid_t fork (void)
    */
    // 拷贝一个子进程  cpu 有可能会先回到子进程也有可能是父进程
    pid_t pid = fork();

    if (pid < 0)
    {
        // 小于0   表示拷贝子进程失败
        perror("cp失败");
        // 退出进程 失败的
        exit(EXIT_FAILURE);
    }
    else if (pid == 0)
    {
        // 表示这是 拷贝的子进程
        printf("我是一个子进程...\n");

        /*
        作用：在当前进程（不再执行一切的程序）转而执行指定的程序
        可以理解 就像是结束了一个进程  重新开始另一个进程
        参数1：可执行文件的路径（自定义的或系统的）
        参数2：包含执行文件路径、要传递的参数的字符串数组 (最后一个元素必须是NULL)
        参数3：包含PATH环境变量的字符串数组（最后一个元素必须是NULL，可以直接为{NULL}表示用默认的）
        返回值
        失败：-1
        成功：函数不返回，后面的代码不会执行

 * int execve (char *__path, char *__argv[], char *__envp[])
*/
        // 要执行文件的参数
        char *gagv[] =
            {
                "ping",
                "-c",
                "10",
                "www.baidu.com",
                NULL};
        // __environ 这个宏返回的是父类的 环境变量  这个父类指的是 pid大于0的那个进程
        int result = execve("/usr/bin/ping",
                            gagv,
                            __environ);
        if(result < 0)
        {
            //到这里表示是没有成功
            perror("exevce");
        }

        //到这里表示 exevce成功  
        //当是成功后 这个进程就不会再执行这个程序 转而区执行 你需要他执行的程序 就是 ping 
        //所以下面的程序是不会被执行到的
        printf("exevce成功后是否执行到这里...\n");
    }
    else
    {
        // 进入这个分支表示是一个父进程
        printf("我是一个父进程,子进程pid = %d\n", pid);

        //在linux中父进程需要负责回收子进程的尸体  或者说父进程有第一时间获得子进程的返回的数据
        //当子进程结束  父进程未结束，未回收子进程尸体，子进程的内存会被释放，
        //但是他的返回信息等一些东西会被保存到内核内存中，这个就算僵尸进程
        
        //当子进程结束  父进程结束，未回收子进程尸体，子进程的内存会被释放，
        //他的尸体会被 第一进程回收

        //wait 或 【waitpid 就算回收尸体
        int result_caild;
        // wait  回收子进程尸体 当有任意子进程结束 就回收
        //  result_caild用来保存子进程的返回直   caild_pid用来保存子进程pid
        //pid_t caild_pid = wait(&result_caild);

        // waitpid  回收指定子进程尸体 
        //参数1  为-1 就是任意子进程
        //参数2  result_caild用来保存子进程的返回直  
        //参数3  WNOHANG表示不会等待子进程退出  如果有子进程以及退出就会回收   否则结束  为0  就会等待
        //返回值   caild_pid用来保存子进程pid 
        //pid_t caild_pid = waitpid(-1,&result_caild,WNOHANG);
        pid_t caild_pid = waitpid(-1,&result_caild,0);

        // sleep(10000);

        printf("子进程pid = %d,返回值为 = %d\n",caild_pid,result_caild);
    }

    return 0;
}
